package com.javacoo.junit.generator.codegen;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.stream.Collectors;

import com.javacoo.junit.generator.api.ProgressCallback;
import com.javacoo.junit.generator.api.plugin.GeneratorPlugin;
import com.javacoo.junit.generator.config.Configuration;
import com.javacoo.junit.generator.config.Context;
import com.javacoo.junit.generator.config.xml.ConfigurationParser;
import com.javacoo.junit.generator.env.ContextEnvment;
import com.javacoo.junit.generator.exception.InvalidConfigurationException;
import com.javacoo.junit.generator.exception.XMLParserException;
import com.javacoo.junit.generator.internal.ObjectFactory;
import com.javacoo.junit.generator.internal.util.StringUtility;

import static com.javacoo.junit.generator.internal.util.ClassloaderUtility.getCustomClassloader;
import static com.javacoo.junit.generator.internal.util.messages.Messages.getString;

/**
 * <p>说明:</p>
 * <li></li>
 *
 * @author duanyong@jccfc.com
 * @date 2021/1/28 22:38
 */
public abstract class AbstractGeneratorPlugin implements GeneratorPlugin {
    /**
     * 默认输出相对路径
     */
    protected static final String BASE_OUTPUT_PACKAGE = "/src/test/java";
    /**
     * 当前上下环境对象
     */
    protected ContextEnvment contextEnvment;
    /**
     * 配置对象
     */
    protected Configuration configuration;
    /**
     * 警告信息
     */
    protected List<String> warnings;
    /**
     * 文件输出路径
     */
    protected String outputDirectoryPath;
    /**
     * 初始化
     * <li></li>
     *
     * @param contextEnvment :上下文环境对象
     * @author duanyong@jccfc.com
     * @date 2021/1/28 15:21
     * @return: GeneratorPlugin
     */
    @Override
    public GeneratorPlugin init(ContextEnvment contextEnvment) throws IOException, XMLParserException, InvalidConfigurationException {
        if (contextEnvment == null) {
            throw new IllegalArgumentException(getString("RuntimeError.23"));
        }
        if (contextEnvment.getCallback() == null) {
            contextEnvment.setCallback(new ProgressCallback() {});
        }
        if (contextEnvment.getWarnings() == null) {
            contextEnvment.setWarnings(new ArrayList<>());
        }
        this.warnings = contextEnvment.getWarnings();

        this.contextEnvment = contextEnvment;

        //构建解析器
        ConfigurationParser configurationParser = new ConfigurationParser(contextEnvment.getExtraProperties(), this.warnings);
        //解析
        this.configuration = configurationParser.parseConfiguration(contextEnvment.getConfigurationFile());
        if (this.configuration == null) {
            throw new IllegalArgumentException(getString("RuntimeError.2"));
        }
        //校验
        this.configuration.validate();
        //输出文件路径
        this.outputDirectoryPath = this.contextEnvment.getBaseDirectory().getAbsolutePath() + BASE_OUTPUT_PACKAGE;

        //初始化后处理
        initAfter();

        ObjectFactory.reset();
        return this;
    }
    /**
     * 执行生成
     * <li></li>
     *
     * @author duanyong@jccfc.com
     * @date 2021/1/28 14:41
     * @return: void
     */
    @Override
    public void generate() {
        //指定的执行上下文ID
        Set<String> targetContextIds = getTargetContexts();
        // 获取待执行上下文
        List<Context> contextsToRun;
        if (targetContextIds.isEmpty()) {
            contextsToRun = this.configuration.getContexts();
        } else {
            contextsToRun = this.configuration.getContexts().stream().filter(context -> targetContextIds.contains(context.getId())).collect(
                Collectors.toList());
        }

        // 设置自定义ClassLoader
        if (!this.configuration.getClassPathEntries().isEmpty()) {
            ClassLoader classLoader = getCustomClassloader(this.configuration.getClassPathEntries());
            ObjectFactory.addExternalClassLoader(classLoader);
        }
        //执行生成
        contextsToRun.stream().forEach(context -> execute(context));
        //完成
        this.contextEnvment.getCallback().done();
    }
    /**
     * 执行生成文件
     * <li></li>
     * @author duanyong@jccfc.com
     * @date 2021/1/5 9:58
     * @param context: 待执行的上下文
     * @return: void
     */
    protected abstract void execute(Context context);

    /**
     * 初始化后处理
     * <li></li>
     * @author duanyong@jccfc.com
     * @date 2021/1/29 9:08
     * @return: void
     */
    protected void initAfter(){}

    /**
     * 获取指定执行的上下文id set
     * <p>说明:</p>
     * <li></li>
     * @author duanyong@jccfc.com
     * @date 2021/1/11 22:37
     */
    private Set<String> getTargetContexts() {
        Set<String> contextsToRun = new HashSet<>();
        if (StringUtility.stringHasValue(contextEnvment.getContexts())) {
            StringTokenizer st = new StringTokenizer(contextEnvment.getContexts(), ",");
            while (st.hasMoreTokens()) {
                String s = st.nextToken().trim();
                if (s.length() > 0) {
                    contextsToRun.add(s);
                }
            }
        }
        return contextsToRun;
    }
    /**
     * 根据类对象获取输出文件路径
     * <li></li>
     * @author duanyong@jccfc.com
     * @date 2021/1/29 10:41
     * @param sourceClass: 目标类对象
     * @param outputFileDir: 输出文件目录
     * @return: java.lang.String 输出文件路径
     */
    protected String getOutFilePath(Class sourceClass,String outputFileDir) {
        Package sourcePackage = sourceClass.getPackage();

        //包路径
        StringBuilder packagePath = new StringBuilder().append(outputFileDir).append("/").append(sourcePackage.getName().replace(".","/")).append("/");
        //生成文件夹
        File filePath = new File(packagePath.toString());
        if (!filePath.exists()){
            filePath.mkdirs();
        }

        //文件名称
        String fileName = new StringBuilder().append(sourceClass.getSimpleName().substring(0, 1).toUpperCase()).append(sourceClass.getSimpleName().substring(1)).toString();
        //输出文件路径
        StringBuilder outFile = packagePath.append(fileName).append("Test.java");

        return outFile.toString();
    }
}
